﻿// Decompiled with JetBrains decompiler
// Type: Netick.ServerSimulation
// Assembly: Netick, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 6726ECA1-C773-4CF7-8952-E81B30D93B52
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.xml

using System;
using System.Collections.Generic;

#nullable disable
namespace Netick;

public sealed class ServerSimulation : Simulation
{
  internal ServerSimulation.ClientData[] Clients;
  internal NetickAllocator[] Snapshots;
  internal NetickAllocator InterpFrom;

  internal ServerSimulation(NetickEngine engine)
    : base(engine)
  {
    this.Snapshots = new NetickAllocator[engine._config.MaxSnapshots];
    this.Clients = new ServerSimulation.ClientData[this.Engine.MaxClients];
    for (int index = 0; index < this.Engine.MaxClients; ++index)
      this.Clients[index] = new ServerSimulation.ClientData(this, index);
    for (int index = 0; index < engine._config.MaxSnapshots; ++index)
    {
      NetickAllocator buffer = new NetickAllocator((long) engine._config.StateAllocatorBlockSize);
      this.Snapshots[index] = buffer;
      this.StateAllocator.Link(buffer);
    }
    this.InterpFrom = new NetickAllocator((long) engine._config.StateAllocatorBlockSize);
    this.StateAllocator.Link(this.InterpFrom);
    this.To = this.StateAllocator._allocator;
    this.From = this.InterpFrom;
  }

  internal override void Reset()
  {
    for (int index = 0; index < this.Clients.Length; ++index)
      this.Clients[index].ResetCSPStartData();
  }

  internal override unsafe void InitEntity(Entity entity, NetickAllocator source, bool isUnliked)
  {
    int blockIndex = entity.BlockIndex;
    long stateOffsetBytes = entity.StateOffsetBytes;
    long sizeBytes = entity.StateSizeWords * 4L;
    for (int index = 0; index < this.Snapshots.Length; ++index)
      MemoryAllocation.Clear((void*) ((IntPtr) this.Snapshots[index].Blocks[blockIndex].Ptr + (IntPtr) stateOffsetBytes), sizeBytes);
    MemoryAllocation.Copy((void*) ((IntPtr) this.Previous.Blocks[blockIndex].Ptr + (IntPtr) stateOffsetBytes), (void*) ((IntPtr) source.Blocks[blockIndex].Ptr + (IntPtr) stateOffsetBytes), sizeBytes);
    MemoryAllocation.Copy((void*) ((IntPtr) this.InterpFrom.Blocks[blockIndex].Ptr + (IntPtr) stateOffsetBytes), (void*) ((IntPtr) source.Blocks[blockIndex].Ptr + (IntPtr) stateOffsetBytes), sizeBytes);
  }

  internal override void Step()
  {
    this.StateAllocator.CopyTo(this.InterpFrom);
    this.UpdateInputLogic(this.Engine.SimulationTick);
    this.InvokeNetworkFixedUpdate();
  }

  internal void OnTickEnd()
  {
    this.StateAllocator.CopyTo(this.Snapshots[this.Engine.SimulationTick % this.Snapshots.Length]);
  }

  private void UpdateInputLogic(Tick simulationTick)
  {
    this.CurrentInput = this.CreateInput(Tick.InvalidTick, Tick.InvalidTick, 0.0f);
    for (int index = 0; index < this.Clients.Length; ++index)
    {
      ServerSimulation.ClientData client = this.Clients[index];
      client.CurrentInput = (SimulationInput) null;
      while (client.RemoteInputs.Count > 0 && client.RemoteInputs.Peek().TargetTick < simulationTick)
        this.RecycleInput(client.RemoteInputs.Dequeue());
      SimulationInput simulationInput;
      if (client.RemoteInputs.TryPeek(ref simulationInput) && simulationInput.TargetTick == simulationTick)
      {
        client.CurrentInput = client.RemoteInputs.Dequeue();
        client.OnConsumeInput();
      }
    }
  }

  internal override void PostStep()
  {
    base.PostStep();
    if (this.CurrentInput != null)
    {
      this.RecycleInput(this.CurrentInput);
      this.CurrentInput = (SimulationInput) null;
    }
    for (int index = 0; index < this.Clients.Length; ++index)
    {
      if (this.Clients[index].CurrentInput != null)
      {
        this.RecycleInput(this.Clients[index].CurrentInput);
        this.Clients[index].CurrentInput = (SimulationInput) null;
      }
    }
  }

  public bool FetchInputFor(int cliIndex, out SimulationInput input, ref bool isDuplicated)
  {
    input = (SimulationInput) null;
    ServerSimulation.ClientData client = this.Clients[cliIndex];
    if (client.CurrentInput == null || client.CurrentInput.Ignore || client.CurrentInput.IsDuplicated && !this.Engine._config.InputReuseAtLowFPS)
      return false;
    isDuplicated = this.CurrentInput.IsDuplicated;
    input = client.CurrentInput;
    return true;
  }

  public unsafe bool FetchInputFor<T>(
    int cliIndex,
    out T input,
    ref bool isDuplicated,
    int localPlayerIndex)
    where T : unmanaged
  {
    input = default (T);
    ServerSimulation.ClientData client = this.Clients[cliIndex];
    if (client.CurrentInput == null || client.CurrentInput.Ignore || client.CurrentInput.IsDuplicated && !this.Engine._config.InputReuseAtLowFPS)
      return false;
    isDuplicated = this.CurrentInput.IsDuplicated;
    SimulationInput currentInput = client.CurrentInput;
    int num = this.Engine._reflectionData.InputTypeToId[typeof (T)];
    for (int index = 0; index < currentInput.Inputs.Count; ++index)
    {
      if (currentInput.Inputs[index].Type == num && currentInput.Inputs[index].PlayerIndex == localPlayerIndex)
      {
        input = *(T*) currentInput.Inputs[index].Ptr;
        return true;
      }
    }
    return false;
  }

  public class ClientData
  {
    internal ServerSimulation Simulation;
    internal int Index;
    public SimulationInput CurrentInput;
    internal Queue<SimulationInput> RemoteInputs;
    internal List<SimulationInput> InputPackCache;
    private const int UsableInputsToStart = 5;
    internal bool ServerReady;
    internal bool ClientReady;
    internal int UsableInputs;
    internal int InputsPerClient;
    internal Tick LastInputTickReceived = Tick.InvalidTick;

    internal bool Ready => this.ServerReady && this.ClientReady;

    internal ClientData(ServerSimulation simulation, int index)
    {
      this.Index = index;
      this.Simulation = simulation;
      this.RemoteInputs = new Queue<SimulationInput>(simulation.Engine._config.MaxSnapshots);
      this.InputPackCache = new List<SimulationInput>(simulation.Engine._config.MaxSnapshots);
      this.InputsPerClient = simulation.Engine._config.MaxSnapshots;
    }

    internal bool ReceiveInput(SimulationInput input)
    {
      if (input.TargetTick <= this.LastInputTickReceived && this.LastInputTickReceived.IsValid)
        return false;
      while (this.RemoteInputs.Count > this.InputsPerClient)
        this.Simulation.RecycleInput(this.RemoteInputs.Dequeue());
      input.Ignore = input.Ignore || !this.Ready;
      this.LastInputTickReceived = input.TargetTick;
      this.RemoteInputs.Enqueue(input);
      return true;
    }

    internal void Reset()
    {
      this.ServerReady = false;
      this.ClientReady = false;
      this.UsableInputs = 0;
      this.LastInputTickReceived = Tick.InvalidTick;
      while (this.RemoteInputs.Count > 0)
        this.Simulation.RecycleInput(this.RemoteInputs.Dequeue());
    }

    internal void ResetCSPStartData()
    {
      this.ServerReady = false;
      this.ClientReady = false;
      this.UsableInputs = 0;
    }

    internal void OnConsumeInput()
    {
      if (this.Ready)
        return;
      ++this.UsableInputs;
      this.ServerReady = this.UsableInputs > 5;
    }

    internal void OnClientReady() => this.ClientReady = true;
  }
}
